package com.inspectortime.webapp.payment;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.persistence.Transient;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class AuthorizeNetPaymentRequestHelper extends AbstractPaymentRequestHelper {

	private static Log log = LogFactory.getLog(AuthorizeNetPaymentRequestHelper.class);
	private String responseBody;

	public String getResponseBody() {
		return responseBody;
	}

	private String loginId;
	private String paymentGatewayUrl = "https://secure.authorize.net/gateway/transact.dll";

	private String transactionKey;
	private String fingerprint;

	@Transient
	public String getFingerprint() {
		return fingerprint;
	}

	public AuthorizeNetPaymentRequestHelper(String loginId, String transactionKey) {
		super();
		this.loginId = loginId;
		this.transactionKey = transactionKey;
	}

	/**
	 * Used to store the request string for debugging purposes (so that
	 * customers can contact Authorize.net with some meaningful info)
	 * 
	 */
	private String executionLog;

	public String getExecutionLog() {
		return executionLog;
	}

	public boolean execute() {

		// By default, this sample code is designed to post to our test server
		// for
		// developer accounts: https://test.authorize.net/gateway/transact.dll
		// for real accounts (even in test mode), please make sure that you are
		// posting to: https://secure.authorize.net/gateway/transact.dll
		// String url = "https://test.authorize.net/gateway/transact.dll";

		// Submit a test request to validate the entered login id and
		// transaction key
		try {
			this.generateFingerprint();
		} catch (GeneralSecurityException e1) {
			log.error("Error creating hash fingerprint from transactionKey "
					+ this.transactionKey + " and login id " + loginId, e1);
		}
		HttpClient client = new HttpClient();
		PostMethod method = new PostMethod(paymentGatewayUrl);
		StringBuffer executionLogBuffer = new StringBuffer();
		final String BR = "\n";
		executionLogBuffer.append("**** REQUEST SUBMITTED TO AUTHORIZE.NET:"
				+ BR);
		executionLogBuffer.append("<FORM action='" + paymentGatewayUrl
				+ "' method='POST'");
		executionLogBuffer.append(BR);
		method.setParameter("x_login", loginId);
		executionLogBuffer.append("<INPUT type='hidden' name='x_login' value='"
				+ loginId + "' />");
		executionLogBuffer.append(BR);
		method.setParameter("x_amount", getAmount());
		executionLogBuffer
				.append("<INPUT type='hidden' name='x_amount' value='" + getAmount()
						+ "' />");
		executionLogBuffer.append(BR);
		method.setParameter("x_description", getDescription());
		executionLogBuffer
				.append("<INPUT type='hidden' name='x_description' value='"
						+ getDescription() + "' />");
		executionLogBuffer.append(BR);
		method.setParameter("x_invoice_num", getInvoiceNumber());
		executionLogBuffer
				.append("<INPUT type='hidden' name='x_invoice_num' value='"
						+ getInvoiceNumber() + "' />");
		executionLogBuffer.append(BR);
		method.setParameter("x_fp_sequence", String.valueOf(getSequenceNumber()));
		executionLogBuffer
				.append("<INPUT type='hidden' name='x_fp_sequence' value='"
						+ String.valueOf(getSequenceNumber()) + "' />");
		executionLogBuffer.append(BR);
		method.setParameter("x_fp_timestamp", String.valueOf(getTimestamp()));
		executionLogBuffer
				.append("<INPUT type='hidden' name='x_fp_timestamp' value='"
						+ String.valueOf(getTimestamp()) + "' />");
		executionLogBuffer.append(BR);
		method.setParameter("x_fp_hash", this.fingerprint);
		executionLogBuffer
				.append("<INPUT type='hidden' name='x_fp_hash' value='"
						+ this.fingerprint + "' />");
		executionLogBuffer.append(BR);
		method.setParameter("x_test_request", String.valueOf(isTestMode()));
		executionLogBuffer
				.append("<INPUT type='hidden' name='x_test_request' value='"
						+ String.valueOf(isTestMode()) + "' />");
		executionLogBuffer.append(BR);
		method.setParameter("x_show_form", "PAYMENT_FORM");
		executionLogBuffer
				.append("<INPUT type='hidden' name='x_show_form' value='PAYMENT_FORM' />");
		executionLogBuffer.append(BR);
		executionLogBuffer.append("</FORM>");
		executionLogBuffer.append(BR + BR
				+ "**** RESPONSE RECEIVED FROM AUTHORIZE.NET:" + BR);
		boolean result = false;
		try {
			client.executeMethod(method);
			String logo_shows_valid_trans_key = "content/SecureCheckout.GIF";
			responseBody = method.getResponseBodyAsString();
			if (method.getStatusText().equals("OK")
					&& responseBody.contains(logo_shows_valid_trans_key)) {
				log.info("Got valid response for login ID " + loginId);
				result = true;
				executionLogBuffer.append(method.getStatusLine());
			} else {
				log
						.info("Got invalid response for login ID "
								+ loginId
								+ ", will dispay request params to user for troubleshooting with authorize.net");

				// Add server response to exec log, but filter out html tags
				String responseStr = new String(method
						.getResponseBodyAsString());
				responseStr = StringUtils.remove(responseStr, "<BODY>");
				responseStr = StringUtils.remove(responseStr, "<HTML>");
				responseStr = StringUtils.remove(responseStr, "</BODY>");
				responseStr = StringUtils.remove(responseStr, "</HTML>");
				executionLogBuffer.append(responseStr);
			}
		} catch (HttpException e) {
			log.error("Caught HttpException", e);
		} catch (IOException e) {
			log.error("Caught IOException", e);
		} finally {
			method.releaseConnection();
			this.executionLog = executionLogBuffer.toString();
		}
		return result;
	}

	public void generateFingerprint() throws GeneralSecurityException {

		StringBuffer strbuf = null;

		KeyGenerator kg = KeyGenerator.getInstance("HmacMD5");
		SecretKey key = new SecretKeySpec(transactionKey.getBytes(), "HmacMD5");
		// A MAC object is created to generate the hash using the HmacMD5
		// algorithm
		Mac mac = Mac.getInstance("HmacMD5");
		mac.init(key);
		String inputstring = loginId + "^" + getSequenceNumber() + "^" + getTimestamp()
				+ "^" + getAmount() + "^";
		byte[] result = mac.doFinal(inputstring.getBytes());
		// Convert the result from byte[] to hexadecimal format
		strbuf = new StringBuffer(result.length * 2);
		for (int i = 0; i < result.length; i++) {
			if (((int) result[i] & 0xff) < 0x10)
				strbuf.append("0");
			strbuf.append(Long.toString((int) result[i] & 0xff, 16));
		}

		this.fingerprint = strbuf.toString();
	}

	public String getLoginId() {
		return loginId;
	}

	/**
	 * The url for authorize.net's payment gateway service where requests are
	 * posted. If you have a developer's test account, this url can also be
	 * used: https://test.authorize.net/gateway/transact.dll
	 * 
	 * @return
	 */
	public String getPaymentGatewayUrl() {
		return paymentGatewayUrl;
	}

	public String getTransactionKey() {
		return transactionKey;
	}

	public void setLoginId(String loginId) {
		this.loginId = loginId;
	}

	public void setTransactionKey(String transactionKey) {
		this.transactionKey = transactionKey;
	}
}
